#if !defined(glib_g2_rotation_h)
#define glib_g2_rotation_h

#include "vector.h"
#include <cmath>

namespace g2 {

template <class T>
class basic_rotation;

typedef basic_rotation<double> rotation;
typedef basic_rotation<float> rotationf;
typedef basic_rotation<long double> rotationl;

template <class T>
class basic_rotation {
private:
    T angle_;
	T cos_, sin_;
public:
	basic_rotation()
		{ }
	basic_rotation(T angle)
		{ this->angle(angle); }
	basic_rotation(T cos, T sin)
		{ cos_sin(cos, sin); }
	basic_rotation(T angle, T cos, T sin)
		{ angle_ = angle; cos_ = cos; sin_ = sin; }
	template <class U> 
	basic_rotation(const basic_rotation<U> & v)
		{ this->angle(v.angle()); } //we need to recalculate cos & sin since U may be of lesser precision
	basic_rotation & identity() 
		{ angle_ = 0; cos_ = 1; sin_ = 0; return * this; }
	void angle(T new_angle) 
		{ angle_ = new_angle; cos_ = std::cos(angle_); sin_ = std::sin(angle_); }
	T angle() const
		{ return angle_; }
	void cos_sin(T cos, T sin) 
		{ angle_ = std::atan2(sin, cos); cos_ = cos; sin_ = sin; }
	T cos() const
		{ return cos_; }
	T sin() const
		{ return sin_; }
	basic_vector<T> operator ()(const basic_vector<T> & v) const 
		{ return basic_vector<T>(cos_ * v.x - sin_ * v.y, sin_ * v.x + cos_ * v.y);	}
	basic_vector<T> inverse(const basic_vector<T> & v) const ///< \return product of inverse rotation and given vector
		{ return basic_vector<T>(cos_ * v.x + sin_ * v.y, -sin_ * v.x + cos_ * v.y);	}
	T det() const
		{ return 1; }
	operator bool() const ///< always true since not degenerate
		{ return true; }
	basic_rotation<T> inverse() const
		{ return basic_rotation(-angle_, cos_, -sin_); }
	T tr() const ///< trace of the matrix
		{ return 2 * cos_; }
public:
	bool operator == (const basic_rotation<T> & v) const
		{ return angle == v.angle; }
	bool operator != (const basic_rotation<T> & v) const
		{ return !(*this == v); }
};

template <class T>
inline basic_vector<T> operator * (const basic_rotation<T> & m, const basic_vector<T> & v) {
    return m(v);
}

template <class T>
inline basic_rotation<T> operator * (const basic_rotation<T> & lhs, const basic_rotation<T> & rhs) {
    return basic_rotation<T>(lhs.angle() + rhs.angle());
}

} //namespace g2

#endif // glib_g2_rotation_h
